package com.tiancheng.trade.order.mq.consumer;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.rabbitmq.client.Channel;
import com.tiancheng.trade.commom.core.data.MessageBody;
import com.tiancheng.trade.commom.core.enums.MessageTypeEnum;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import com.tiancheng.trade.commom.core.utils.JsonUtil;
import com.tiancheng.trade.order.bo.cmq.RefundDetailResultMsgBO;
import com.tiancheng.trade.order.bo.cmq.RefundResultMsgBO;
import com.tiancheng.trade.order.bo.notify.PayResultOrderBO;
import com.tiancheng.trade.order.configuration.CallbackConfig;
import com.tiancheng.trade.order.model.Order;
import com.tiancheng.trade.order.model.RefundOrders;
import com.tiancheng.trade.order.model.RefundOrdersDetail;
import com.tiancheng.trade.order.service.BillService;
import com.tiancheng.trade.order.service.OrderService;
import com.tiancheng.trade.order.service.RefundOrdersDetailService;
import com.tiancheng.trade.order.service.RefundOrdersService;
import com.tiancheng.trade.order.util.EnumUtil;
import com.tiancheng.trade.order.util.cmq.MessageParsing;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.MessageConversionException;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @Author: kellen
 * @Description: 订单状态回调处理器
 * @Date: create in 2024/11/26 15:45
 */
@Slf4j
@Component
public class OrderStatusCallbackListener {

    @Resource
    private MessageParsing messageParsing;
    @Resource
    private OrderService orderService;
    @Resource
    private RefundOrdersService refundOrdersService;
    @Resource
    private RefundOrdersDetailService refundOrdersDetailService;
    @Resource
    private BillService billService;
    @Resource
    private CallbackUtil callbackUtil;
    @Resource
    private CallbackConfig callbackConfig;
    @Resource
    private RabbitTemplate rabbitTemplate;

    @RabbitListener(queues = {"#{@callbackConfig.getConfigs().![queue]}"})
    public void handleMessage(Message message, Channel channel) throws IOException {
        MessageProperties properties = message.getMessageProperties();
        String messageId = null;
        try {
            // 检查是否是重试消息
            Integer retryCount = properties.getHeader("x-retry-count");
            MessageBody messageBody = convertMessage(message);
            log.info("订单中心回调业务系统[retryCount={}] message:{}",retryCount, JsonUtil.toJsonString(messageBody));
            messageId = messageBody.getMessageId();
            // 调用业务系统接口
            boolean success = processMessage(messageBody,retryCount);

            if (success) {
                channel.basicAck(properties.getDeliveryTag(), false);
            } else {
                // 拒绝消息并发送到重试队列
                channel.basicReject(properties.getDeliveryTag(), false);
                handleRetry(message, messageBody);
            }
        } catch (Exception e) {
            log.error("处理消息异常 [messageId={}]", messageId, e);
            // 拒绝消息并发送到重试队列
            channel.basicReject(properties.getDeliveryTag(), false);
            if (messageId != null) {
                handleRetry(message, null);
            }
        }
    }

    private void handleRetry(Message message, MessageBody messageBody) {
        MessageProperties properties = message.getMessageProperties();
        int retryCount = properties.getHeader("x-retry-count") == null ?
                0 : (int) properties.getHeader("x-retry-count");
        String routingKey = properties.getReceivedRoutingKey();
        String txId = messageBody != null ? messageBody.getMessageId() : "unknown";

        if (retryCount >= getMaxAttempts(properties)) {
            log.error("超出重试次数，消息丢弃了！[messageId={}]", txId);
            return;
        }
        long delay = calculateDelay(retryCount, properties);
        try {
            // 创建新消息，发送到对应业务的重试队列
            Message retryMessage = MessageBuilder
                    .withBody(message.getBody())
                    .setHeader("x-retry-count", retryCount + 1)
                    .setExpiration(String.valueOf(delay)) // 设置消息的过期时间
                    .build();
            // 发送到重试交换器，使用相同的路由键
            rabbitTemplate.send(callbackConfig.getRetryExchange(), routingKey, retryMessage);
        } catch (Exception e) {
            log.error("发送消息到重试队列失败 [messageId={}]", txId, e);
        }
    }

    private MessageBody convertMessage(Message message) throws IOException {
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.registerModule(new JavaTimeModule());
            return objectMapper.readValue(message.getBody(), MessageBody.class);
        } catch (JsonProcessingException e) {
            throw new MessageConversionException("消息格式错误", e);
        }
    }
    /**
     * 处理消息
     *
     * @param messageBody
     */
    private boolean processMessage(MessageBody messageBody, Integer retryCount) {
        String messageType = messageBody.getMessageType();
        if (StringUtils.isEmpty(messageType)) {
            log.error("接收到的订单回调消息类型为空！");
            return true;
        }
        MessageTypeEnum msgType = EnumUtil.getEnumByCode(MessageTypeEnum.class, messageType);
        if (msgType == null) {
            log.error("未查询到指定消息类型");
            return true;
        }
        final Optional<MessageTypeEnum> messageTypeEnumOp = MessageTypeEnum.getByCode(messageBody.getMessageType());
        if (messageTypeEnumOp.isEmpty()) {
            log.error("未知的消息类型：{}", messageBody.getMessageType());
            return true;
        }
        try {
            switch (messageTypeEnumOp.get()) {
                case order_refund:
                    // 自动发起退款
                    break;
                case order_pay_result:
                    // 订单支付结果
                    PayResultOrderBO payNotifyBO = messageParsing.getData(messageBody, PayResultOrderBO.class);
                    final Order order = queryByOrderId(payNotifyBO.getOrderId());
                    log.info("订单支付回调消息解析完成:{}", payNotifyBO);
                    if (Objects.nonNull(order) && StringUtils.isNotEmpty(order.getNotifyUrl())) {
                        payNotifyBO.setOrderId(order.getOrderId());
                        try {
                            // 先创建交易中台的账单，再回调业务系统
                            billService.createAfterPay(order, payNotifyBO);
                        } catch (Exception e) {
                            log.error("生成账单异常：", e);
                            throw e;
                        }
                        final boolean isSc = callbackUtil.callTarget(order.getNotifyUrl(), MessageTypeEnum.order_pay_result, payNotifyBO);
                        BusinessErrorInfoEnum.SYSTEM_ERROR.assertIsTrue(isSc, "消息推送失败！");
                    }
                    break;
                case order_refund_result:
                    // 订单退款结果
                    RefundResultMsgBO refundResultMsgBO = messageParsing.getData(messageBody, RefundResultMsgBO.class);
                    log.info("订单退款回调消息解析完成:{}", refundResultMsgBO);
                    final RefundOrders refundOrders = refundOrdersService.getByRefundId(refundResultMsgBO.getRefundId());
                    final Order order1 = queryByOrderId(refundOrders.getOrderId());
                    if (Objects.nonNull(order1) && StringUtils.isNotEmpty(order1.getNotifyUrl())) {
                        refundResultMsgBO.setOrderId(order1.getOrderId());
                        final List<RefundOrdersDetail> refundOrdersDetails = refundOrdersDetailService.queryByRefundId(refundResultMsgBO.getRefundId());
                        final List<RefundDetailResultMsgBO> refundDetailResultMsgBOS = refundOrdersDetails.stream().map(r -> {
                            final RefundDetailResultMsgBO refundDetailResultMsgBO = new RefundDetailResultMsgBO();
                            refundDetailResultMsgBO.setAmount(r.getRefundAmount());
                            refundDetailResultMsgBO.setRefundAmount(r.getRefundApplyAmount());
                            refundDetailResultMsgBO.setStatus(r.getRefundStatus());
                            refundDetailResultMsgBO.setSubOrderId(r.getSubOrderId());
                            refundDetailResultMsgBO.setSubPaymentId(r.getSubPaymentId());
                            refundDetailResultMsgBO.setSubRefundId(r.getSubRefundId());
                            return refundDetailResultMsgBO;
                        }).collect(Collectors.toList());
                        refundResultMsgBO.setSubRefunds(refundDetailResultMsgBOS);
                        try {
                            // 先创建交易中台的账单，再回调业务
                            billService.createBillAfterRefund(order1, refundResultMsgBO, refundOrdersDetails);
                            // 发起退款单核销
                            // 退款成功，核销退款单
                            billService.refundVerification(refundOrdersDetails);
                        } catch (Exception e) {
                            log.error("生成退款账单异常：", e);
                        }
                        final boolean isSc = callbackUtil.callTarget(order1.getNotifyUrl(), MessageTypeEnum.order_refund_result, refundResultMsgBO);
                        BusinessErrorInfoEnum.SYSTEM_ERROR.assertIsTrue(isSc, "消息推送失败！");
                    }
                    break;
                case order_coupon:
                    // 核销、释放优惠券
                    break;
            }
            return true;
        } catch (Exception e) {
            log.error("回调业务系统异常：", e);
            return false;
        }
    }

    /**
     * 查询订单信息
     *
     * @param orderId
     * @return
     */
    public Order queryByOrderId(String orderId) {
        return orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderId, orderId));
    }
    private int getMaxAttempts(MessageProperties properties) {
        return callbackConfig.getMaxAttempts();
    }

    private long calculateDelay(int retryCount, MessageProperties properties) {
        CallbackConfig.BizConfig config = getConfigByRoutingKey(
                properties.getReceivedRoutingKey());

        double delay = callbackConfig.getInitialInterval() * Math.pow(callbackConfig.getMultiplier(), retryCount);
        return (long) Math.min(delay, callbackConfig.getMaxInterval());
    }
    private CallbackConfig.BizConfig getConfigByRoutingKey(String routingKey) {
        return callbackConfig.getConfigs().stream()
                .filter(c -> c.getRoutingKey().equals(routingKey))
                .findFirst()
                .orElseThrow(() -> new RuntimeException(
                        "未找到路由键对应的配置: " + routingKey));
    }


}
